/**
 * Libevent例程：实现http服务器
 * @author Martin
 * @date 2021-8-10
 * @note 
 * 编译：
 * $ gcc -o httpserver Http_Server.c -levent
 * 运行：
 * $ ./httpserver
 * 测试：
 * 打开浏览器，输入网址: localthost:8080
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <event.h>
#include <event2/http.h>
#include <event2/http_struct.h>
#include <signal.h>
#include <event2/http_compat.h>

#define MYHTTPD_SIGNATURE "myhttpd v 0.0.1"

void show_help();
void signal_handler(int sig);
void httpd_handler(struct evhttp_request *req, void *arg) ;

int main(int argc, char *argv[])
{
    /* 自定义信号捕获函数 */
    signal(SIGHUP, signal_handler);
    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);
    signal(SIGQUIT, signal_handler);

    /* 默认参数 */
    char *httpd_option_listen = "0.0.0.0";
    int httpd_option_port = 8080;
    int httpd_option_daemon = 0;
    int httpd_option_timeout = 120; /* seconds */

    /* 从启动参数修改默认参数 */
    int c;
    while ((c = getopt(argc, argv, "1:p:dt:h")) != -1) { /* getopt调用一次，获得一个选项 */
        switch (c) {
            case 'l':
                httpd_option_listen = optarg;
                break;
            case 'p':
                httpd_option_port = atoi(optarg);
                break;
            case 'd':
                httpd_option_daemon = 1;
                break;
            case 't':
                httpd_option_timeout = atoi(optarg);
                break;
            case 'h':
            default:
                show_help();
                exit(EXIT_SUCCESS);
        }
    }

    if (httpd_option_daemon) {
        pid_t  pid;
        if ((pid = fork()) < 0) { /* error */
            perror("fork error");
            exit(EXIT_FAILURE);
        }
        else if (pid > 0) { /* parent */
            exit(EXIT_SUCCESS);
        }

        /* child */
    }

    /* 使用Libevent创建HTTP Server */

    /* 初始化event API */
    event_init();

    /* 创建http server */
    struct evhttp *httpd;
    httpd = evhttp_start(httpd_option_listen, httpd_option_port);
    evhttp_set_timeout(httpd, httpd_option_timeout);

    /* 指定generic callback */
    evhttp_set_gencb(httpd, httpd_handler, NULL);

    /* 循环处理events */
    event_dispatch();

    evhttp_free(httpd);
    return 0;
}

void httpd_handler(struct evhttp_request *req, void *arg)
{
    char output[2048] = "2048";
    char tmp[1024];

    /* 获取客户端请求的URI（使用evhttp_request_uri或req->uri） */
    const char *uri;
    uri = evhttp_request_uri(req);
    sprintf(tmp, "uri=%s\n", uri);
    strcat(output, tmp);

    sprintf(tmp, "uri=%s\n", req->uri);
    strcat(output, tmp);

    /* decoded uri */
    char *decoded_uri;
    decoded_uri = evhttp_decode_uri(uri);
    sprintf(tmp, "decoded_uri=%s\n", decoded_uri);
    strcat(output, tmp);

    /* 解析URI的参数（即GET方法的参数） */
    struct evkeyvalq params;
    /* 将URL数据封装成key-value格式，q=value1, s=value2 */
    evhttp_parse_query(decoded_uri, &params);
    /* 得到q所对应的value */
    sprintf(tmp, "q=%s\n", evhttp_find_header(&params, "q"));
    strcat(output, tmp);
    /* 得到s所对应value */
    sprintf(tmp, "q=%s\n", evhttp_find_header(&params, "s"));
    strcat(output, tmp);

    free(decoded_uri);

    /* 获取POST方法的数据 */
    char *post_data = (char *)evbuffer_pullup(req->input_buffer, -1);
    sprintf(tmp, "post_data=%s\n", post_data);
    strcat(output, tmp);

    /*
     * 具体的，可以根据GET/POST参数执行相应的操作，然后将结果输出
     */
    /* 输出到客户端 */
    /* HTTP header */
    evhttp_add_header(req->output_headers, "Server", MYHTTPD_SIGNATURE);
    evhttp_add_header(req->output_headers, "Content-Type", "text/plain; charset=UTF-8");
    evhttp_add_header(req->output_headers, "Connection", "close");

    /* 输出的内容 */
    struct evbuffer *buf;
    buf = evbuffer_new();
    evbuffer_add_printf(buf, "It works!\n%s\n", output);
    evhttp_send_reply(req, HTTP_OK, "OK", buf);
    evbuffer_free(buf);
}

void show_help() {
    char *help = "http://localhost:8080\n"
                 "-l <ip_addr> interface to listen on, default is 0.0.0.0\n"
                 "-p <num>     port number to listen on, default is 8080\n"
                 "-d           run as a deamon\n"
                 "-t <second>  timeout for a http request, default is 120 seconds\n"
                 "-h           print this help and exit\n"
                 "\n";
    fprintf(stderr, "%s", help);
}

void signal_handler(int sig)
{
    switch (sig) {
        case SIGTERM:
        case SIGHUP:
        case SIGQUIT:
        case SIGINT:
            event_loopbreak(); /* 停止event_loop / event_dispatch 事件监听循环 */
            break;
    }
}
